#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;
using RoboTrocity;
using RoboTrocity.Code;
using RoboTrocity.Code.GameStates;
using RoboTrocity.Code.Items;
using RoboTrocity.Code.ObjectStates;
using RoboTrocity.Code.Props;

#endregion

namespace RoboTrocity
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game : Microsoft.Xna.Framework.Game
    {
		public const float GRAVITY = 20;//9.81f;    //meters per (second squared)
		public const String contentLoc = "Content/";


        GraphicsDeviceManager graphics;
        ContentManager content;
        KeyboardState keyState, prevKeyState;
		MouseState mouseState, prevMouseState;
        SpriteBatch spriteBatch;    //this is the object that draws the sprites
        Map gameMap = new Map();
        Player player;
		Camera gameCamera;
		WaypointManager WPManager;
		Enemy spikey;
		BinarySwitch flippy;

    	private List<GameState> stateList;

		public Game()
        {
            graphics = new GraphicsDeviceManager(this);
            content = new ContentManager(Services);
            gameMap = new Map();

			stateList = new List<GameState>();
        }


        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            base.Initialize();
			InitObjects();
        }

        private void InitObjects()
        {
			gameCamera = new Camera();
			WPManager = new WaypointManager();

			player = EntityFactory.getPlayer();
			gameMap.addObject(player);

			Ladder ladder = new Ladder(new Point(50, 400));
			gameMap.addObject(ladder);

			//Enemy enemy = EntityFactory.getTypeOneEnemy();
			//gameMap.addObject(enemy);

        	Scrap scrap = ItemFactory.GetOneScrap(new Point(200, 200));
        	gameMap.addObject(scrap);

			Trigger testTrigger = new Trigger();
			//testTrigger.addProp(testProp);

			flippy = new BinarySwitch(new Point(0, 400), "switch");
			flippy.AttachedTrigger = testTrigger;
			//gameMap.addObject(flippy);

			WaypointCollection collection = new WaypointCollection();
			collection.addWaypoint(new Waypoint(new Point(450, 450)));
			collection.addWaypoint(new Waypoint(new Point(550, 450)));
			collection.addWaypoint(new Waypoint(new Point(650, 450)));
			WPManager.addWPC(collection);
			//spikey.ai.WPCollection = collection;
			
			PlayState state = new PlayState(PushState, PopState, ref gameMap, ref player, ref gameCamera);
			PushState(state);
			gameMap.loadMap(content, "map");
        }

        /// <summary>
        /// Load your graphics content.  If loadAllContent is true, you should
        /// load content from both ResourceManagementMode pools.  Otherwise, just
        /// load ResourceManagementMode.Manual content.
        /// </summary>
        protected override void LoadContent() {
            spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
        	string[] texNames = new string[] {
        	                                	"brick", "slope", "leftSlope", "playerStanding", "playerJumping",
        	                                	"playerFalling", "playerWalking", "bullet", "spikeGuy", "Enemy1", "switch",
        	                                	"bullet1", "ladder", "playerClimbing", "scrap"
        	                                };
        	String[] fontNames = new String[] {"TestFont"};
        	
			//GraphicsDispenser.TextureNames.CopyTo(texNames, 0);
			
			foreach (String texName in texNames) {
				//GraphicsDispenser.loadTexture(texName, content.Load<Texture2D>(contentLoc + texName));
				GraphicsDispenser.addTexture(texName, content.Load<Texture2D>(contentLoc + texName));
			}

			foreach (String fontName in fontNames) {
				GraphicsDispenser.AddFont(fontName, Content.Load<SpriteFont>(contentLoc + fontName));
			}
			
			InitMapGraphics();
			
        }

        /// <summary>
        /// Unload your graphics content.  If unloadAllContent is true, you should
        /// unload content from both ResourceManagementMode pools.  Otherwise, just
        /// unload ResourceManagementMode.Manual content.  Manual content will get
        /// Disposed by the GraphicsDevice during a Reset.
        /// </summary>
        /// <param name="unloadAllContent">Which type of content to unload.</param>
        protected override void UnloadGraphicsContent(bool unloadAllContent)
        {
            if (unloadAllContent)
            {
                content.Unload();
            }
        }

		/// <summary>
		/// Initialize all the graphics in the map by giving them textures from the GraphicsDispenser.
		/// </summary>
		public void InitMapGraphics() {
			foreach (GameObject gObject in gameMap.ObjectList.ObjectArray) {
				gObject.CurrentState.StateSprite.Texture = GraphicsDispenser.getTexture(gObject.CurrentState.StateSprite.ImgLoc);
				foreach (ObjectState state in gObject.StateList) {
					state.StateSprite.Texture = GraphicsDispenser.getTexture(state.StateSprite.ImgLoc);
				}
			}
		}

		public void PushState(GameState stateToPush) {
			if (stateToPush != null) {
				stateList.Insert(0, stateToPush);
			}
		}

		public void PopState() {
			//pop GameState	
			if (stateList.Count > 0) {
				stateList.RemoveAt(0);
			}
		}

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime) {
			float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
	
			ProcessInput();   //no reason to add this line to all the states
       
			//go through the stateList, update the top states.
        	List<GameState> updateList = new List<GameState>();
			//compile the list of states to update
			foreach(GameState gState in stateList) {
				updateList.Add(gState);
				if (!gState.UpdateThrough) {
					break;
				}
			}
			//update the states
        	foreach (GameState state in updateList) {
        		state.Update(elapsed);
        	}
        	base.Update(gameTime);
		}

		/// <summary>
		/// Processes player input.
		/// </summary>
		public void ProcessInput() {
			prevKeyState = keyState;
			keyState = Keyboard.GetState();
			prevMouseState = mouseState;
			mouseState = Mouse.GetState();

			List<GameState> inputList = new List<GameState>();
			//compile the list of states to update
			foreach (GameState gState in stateList) {
				inputList.Add(gState);
				if (!gState.ProcessThrough) {
					break;
				}
			}
			//update the states
			foreach (GameState state in inputList) {
				state.ProcessInput(keyState, prevKeyState, mouseState, prevMouseState);
			}
		}

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime) {
			float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
        	IsMouseVisible = true;

            spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.BackToFront, SaveStateMode.None);
			//go through the stateList, update the top states.
			List<GameState> drawList = new List<GameState>();
			//compile the list of states to update
			foreach (GameState gState in stateList) {
				drawList.Add(gState);
				if (!gState.DrawThrough) {
					break;
				}
			}
			//update the states
			foreach (GameState state in drawList) {
				state.Draw(spriteBatch);
			}
			

			spriteBatch.End();
            base.Draw(gameTime);
        }
    }
}
